package com.lancoo.cvas.arrangecourse.rulecheck;

import com.lancoo.cvas.arrangecourse.common.StrUtil;
import com.lancoo.cvas.arrangecourse.entities.BaseRule;
import com.lancoo.cvas.arrangecourse.entities.ClassHour;
import com.lancoo.cvas.arrangecourse.entities.TimeslotRoom;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

/**
 * @Description 规则匹配检查父类，提供通用调用函数的支持
 * @Author Fortysun
 * @Date 2023/8/24 9:54
 **/
@Slf4j
public  class RuleCheck {

    protected BaseRule baseRule;

    /**
     *  从课表的map中，取出指定某天的数据
     * @param curDayNo 指定的某天，01周一、02周二。。。以此类推
     * @param classHourMap 当前已排好课的课表的数据
     * @return
             */
    protected List<ClassHour> getCurDayList(String curDayNo,
                    ConcurrentHashMap<String, CopyOnWriteArrayList<ClassHour>> classHourMap) {
        //定义返回的列表信息
        List<ClassHour> retList = new ArrayList<>();
        if (classHourMap != null
                && !classHourMap.isEmpty()) {
            //此处应该筛选出，对应timeslotRoom，某天的课表
            for (Map.Entry<String, CopyOnWriteArrayList<ClassHour>> entry : classHourMap.entrySet()) {
                //筛选出指定某天的课程
                if (entry.getKey().substring(0, 2).equals(curDayNo)) {
                    retList.addAll(entry.getValue());
                }
            }
        }

        return retList;
    }

    /**
     *  从课表的map中，取出指定半天的数据
     * @param curDayNo 指定的某天，01周一、02周二。。。以此类推
     * @param periodType 指定半天的标识，1上午、2下午、3晚上
     * @param classHourMap 当前已排好课的课表的数据
     * @return
     */
    protected List<ClassHour> getCurHalfDayList(String curDayNo,
                        Integer periodType,
                        ConcurrentHashMap<String, CopyOnWriteArrayList<ClassHour>> classHourMap) {
        //定义返回的列表信息
        List<ClassHour> retList = null;
        retList = getCurDayList(curDayNo, classHourMap);

        if (retList != null && !retList.isEmpty()) {
            //3、处理教师上课的集中分散，需要获取与待分配的教室课时分组信息相同的，半天内的数据
            retList = retList.stream()
                    .filter(classHour -> classHour.getTimeslotRoom().getPeriodType().equals(periodType))
                    .collect(Collectors.toList());
        }

        return retList;
    }

    /**
     *  从课表的map中，取出指定半天，其他校区的数据,主要用来跨校区排课
     * @param curDayNo 指定的某天，01周一、02周二。。。以此类推
     * @param periodType 指定半天的标识，1上午、2下午、3晚上
     * @param classHourMap 当前已排好课的课表的数据
     * @param campusId 当前校区的id
     * @return
     */
    protected List<ClassHour> getCurHalfDayOtherCampusList(String curDayNo,
                               Integer periodType,
                               ConcurrentHashMap<String, CopyOnWriteArrayList<ClassHour>> classHourMap,
                               String campusId) {
        //定义返回的列表信息
        List<ClassHour> retList = null;
        retList = getCurDayList(curDayNo, classHourMap);

        if (retList != null && !retList.isEmpty()) {
            //3、处理教师上课的集中分散，需要获取与待分配的教室课时分组信息相同的，半天内的数据
            retList = retList.stream()
                    //获取与待分配的教室课时分组信息相同的，半天内的其他校区的数据
                    .filter(classHour -> !classHour.getTimeslotRoom().getCampusId().equals(campusId))
                    .filter(classHour -> classHour.getTimeslotRoom().getPeriodType().equals(periodType))

                    .collect(Collectors.toList());
        }

        return retList;
    }

    /**
     *  从课表的map中，取出指定半天，其他校区的数据,主要用来跨校区排课
     * @param curDayNo 指定的某天，01周一、02周二。。。以此类推
     * @param classHourMap 当前已排好课的课表的数据
     * @param campusId 当前校区的id
     * @return
     */
    protected List<ClassHour> getCurOneDayOtherCampusList(String curDayNo,
                                                           ConcurrentHashMap<String, CopyOnWriteArrayList<ClassHour>> classHourMap,
                                                           String campusId) {
        //定义返回的列表信息
        List<ClassHour> retList = null;
        retList = getCurDayList(curDayNo, classHourMap);

        if (retList != null && !retList.isEmpty()) {
            //3、处理教师上课的集中分散，需要获取与待分配的教室课时分组信息相同的，半天内的数据
            retList = retList.stream()
                    //获取与待分配的教室课时分组信息相同的，半天内的其他校区的数据
                    .filter(classHour -> !classHour.getTimeslotRoom().getCampusId().equals(campusId))
                    .collect(Collectors.toList());
        }

        return retList;
    }

    /**
     * 从课表的map中，取出与指定某天，相邻的两天，的课表数据
     * @param curDayNo 指定的某天，01周一、02周二。。。以此类推
     * @param classHourMap 当前已排好课的课表的数据
     * @return
     */
    protected List<ClassHour> getPreNext2DayList(String curDayNo,
                 ConcurrentHashMap<String, CopyOnWriteArrayList<ClassHour>> classHourMap) {
        //定义返回的列表信息
        List<ClassHour> retList = new ArrayList<>();
        if (classHourMap != null
                && !classHourMap.isEmpty()) {

            try {
                //将编号转换成整型，dayNo的范围时01、02、03、04、05、06、07
                Integer curDayCode = Integer.parseInt(curDayNo);
                //如果时周一，向前一天时周日07
                Integer preDayCode = curDayCode-1 == 0?7:curDayCode-1;
                String preDayNo = "0"+preDayCode;
                //如果是周日，向后一天是周一01
                Integer nextDayCode = curDayCode+1>7?1:curDayCode+1;
                String nextDayNo = "0"+nextDayCode;

                //此处应该筛选出，对应timeslotRoom，半天内的课程
                for (Map.Entry<String, CopyOnWriteArrayList<ClassHour>> entry : classHourMap.entrySet()) {
                    //获取当前key所在的天的编号，01周一、02周二。。。以此类推
                    String dayNo = entry.getKey().substring(0, 2);
                    //筛选出指定某天的前一天和后一天的课表信息
                    if (dayNo.equals(preDayNo)
                            || dayNo.equals(nextDayNo)) {
                        retList.addAll(entry.getValue());
                    }
                }
            } catch (Exception ex) {
                log.error("#RuleCheck.getNext2DayList# : 天字符串类型转换失败~！！");
                return new ArrayList<>();
            }

        }

        return retList;
    }

    /**
     * 判断当前分配的教室课时分组，是否存在教师和学生冲突的情况
     * @param teacherIds 待分配的教学班的上课教师id，多个以逗号分隔
     * @param studentIds 待分配的教学班的上课学生id，多个以逗号分隔
     * @param phyClassId 行政班id
     * @param classHourList 课时列表信息（如果是跨校区排课包括待分配课时对应的，半天内的课表课时列表信息；
     *                      如果是各校区独立排课，仅仅包括当前课时编码的课时列表信息）
     * @return
     */
    protected boolean isClashes(Long teachingClassId,
                                String teacherIds,
                                String studentIds,
                                String phyClassId,
                                List<ClassHour> classHourList) {
        if (classHourList == null || classHourList.isEmpty()) {
            return false;
        }

        for (ClassHour classHour : classHourList) {
            //如果存在教师或者学生冲突
            if (teachingClassId.equals(classHour.getCourseUnit().getTeachingClassId())
                    ||StrUtil.isStrClashes(teacherIds, classHour.getCourseUnit().getTeacherIds())
                    || StrUtil.isStrClashes(phyClassId, classHour.getCourseUnit().getPhyClassId())
                    || StrUtil.isStrClashes(studentIds, classHour.getCourseUnit().getStudentIds())) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判读课时是否是连续的
     * 判断的标准：1、前后两个对象的课时编号，需要是联系的，即后一个的编号，减去前一个的编号，值等于1，因为需要是1、2、3、4
     * @param connectSection 连上节次
     * @param continuousList 连上的教室课时分组信息列表
     * @return
     */
    protected boolean isTimeslotContinuous(Integer connectSection,
                                         LinkedList<TimeslotRoom> continuousList) {
        //如果连上的教室课时分组列表信息为空，或者没有内容
        if (continuousList == null || continuousList.isEmpty()) {
            return false;
        }

        if (connectSection == 1) {
            //连上一节，不需要判断教室课时分组的连续性了
            return true;
        }
        int size = continuousList.size()-1;
        for (int i = 0; i < size; i++) {
            //当前的教室课时分组对象
            TimeslotRoom current = continuousList.get(i);
            //列表中下一个教室课时分组对象
            TimeslotRoom next = continuousList.get(i+1);


            //如果下一个的课时编号，减去上一个的课时编号，不等于1，说明是不连续的；
            //比如，第2节的编号是2，第一节的编号是1，2-1=1
            if (1 != (next.getPeriod() - current.getPeriod())) {
                //课时分组不连续，不能安排
                return false;
            }
        }
        //可以安排
        return true;
    }
}
